package internal.parser;

import static internal.tokenizer.TokenKeyword.TK_ELSE;
import static internal.tokenizer.TokenKeyword.TK_FUN;
import static internal.tokenizer.TokenKeyword.TK_IF;
import static internal.tokenizer.TokenKeyword.TK_RETURN;
import static internal.tokenizer.TokenKeyword.TK_WHILE;
import static internal.tokenizer.TokenSymbol.TK_COMMA;
import static internal.tokenizer.TokenSymbol.TK_DIVIDE;
import static internal.tokenizer.TokenSymbol.TK_DOT;
import static internal.tokenizer.TokenSymbol.TK_EQ;
import static internal.tokenizer.TokenSymbol.TK_GE;
import static internal.tokenizer.TokenSymbol.TK_GT;
import static internal.tokenizer.TokenSymbol.TK_LBRACE;
import static internal.tokenizer.TokenSymbol.TK_LE;
import static internal.tokenizer.TokenSymbol.TK_LPARENS;
import static internal.tokenizer.TokenSymbol.TK_LT;
import static internal.tokenizer.TokenSymbol.TK_MINUS;
import static internal.tokenizer.TokenSymbol.TK_NE;
import static internal.tokenizer.TokenSymbol.TK_PLUS;
import static internal.tokenizer.TokenSymbol.TK_RBRACE;
import static internal.tokenizer.TokenSymbol.TK_RPARENS;
import static internal.tokenizer.TokenSymbol.TK_SEMICOLON;
import static internal.tokenizer.TokenSymbol.TK_TIMES;
import static tokenizer.TokenClass.TC_IDENTIFIER;
import static tokenizer.TokenClass.TC_LITERAL_DECIMAL;
import static tokenizer.TokenClass.TC_LITERAL_INTEGER;
import internal.tokenizer.TokenDecimal;
import internal.tokenizer.TokenIdentifier;
import internal.tokenizer.TokenInteger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import parser.IParse.IAssignment;
import parser.IParse.IBooleanExpression;
import parser.IParse.ICompoundStatement;
import parser.IParse.IDecimalLiteral;
import parser.IParse.IExpression;
import parser.IParse.IFunction;
import parser.IParse.IIfThenElse;
import parser.IParse.ILiteral;
import parser.IParse.IReturn;
import parser.IParse.ISimpleStatement;
import parser.IParse.ITerm;
import parser.IParse.IVariable;
import parser.IParse.IWhile;
import parser.IParseFactory;
import parser.ParserException;
import tokenizer.IToken;
import tokenizer.TokenClass;

class Parser extends AbstractParser {

	// Some code provided, March 27, 2012 - BJ

	private static final String ERR_INVALID_BOOLEXPR = "Missing relational opr.";
	private static final String ERR_INVALID_ASSEXPR = "Missing assign opr.";
	private static final String ERR_INVALID_RETEXPR = "Missing return keyword";
	private static final String ERR_INVALID_WHILEEXPR = "Missing while keyword";
	private static final String ERR_INVALID_STATEMENT = "Invalid Statement";
	private final IParseFactory factory;

	Parser(final IParseFactory factory) {

		this.factory = factory;
	}

	@Override
	public IFunction doParse() throws ParserException {

		return function();
	}

	// function --> 'fun' ident '(' idlist ')' cmpdstmt '.'
	private IFunction function() throws ParserException {
		// chk
		check(TK_FUN);
		final String identifier = identifier();
		final List<IVariable> parameters = parameterList();

		final ICompoundStatement body = compoundStatement();
		check(TK_DOT);
		checkEOS();
		return factory.newFunction(identifier, parameters, body);
	}

	// bexpr --> aexpr REL_OP aexpr
	// REL_OP --> '>' | '<' | '=' | '<>' | '<=' | '>='

	private List<IVariable> parameterList() throws ParserException {
		// TODO Auto-generated method stub
		List<IVariable> listVar = new ArrayList<IVariable>();
		check(TK_LPARENS);
		while (true) {

			check(TC_IDENTIFIER);

			IVariable tempVar = variable();

			listVar.add(tempVar);

			if (!test(TK_COMMA))
				break;
		}
		check(TK_RPARENS);

		return listVar;
	}

	private IVariable variable() throws ParserException {
		// TODO Auto-generated method stub

		return factory.newVariable(token.getText());
	}

	private String identifier() throws ParserException {
		// TODO Auto-generated method stub
		check(TC_IDENTIFIER);
		return token.getText();
	}

	private IBooleanExpression booleanExpression() throws ParserException {

		final IExpression left = expression();
		if (test(TK_GE) || test(TK_GT) || test(TK_LE) || test(TK_LT)
				|| test(TK_EQ) || test(TK_NE)) {
			final IToken operator = token;
			final IExpression right = expression();
			return factory.newBooleanExpression(left, operator, right);
		}
		die(ERR_INVALID_BOOLEXPR);
		return null;
	}

	private IReturn returnExpression() throws ParserException {
		final IExpression expr = expression();
		check(TK_SEMICOLON);
		return factory.newReturn(expr);
	}

	private IExpression expression() throws ParserException {
		// TODO Auto-generated method stub
		if (test(TK_LPARENS)) {
			IExpression leftExpr = expression();
			check(TK_RPARENS);
			if(test(TK_MINUS) || test(TK_PLUS) || test(TK_DIVIDE)
					|| test(TK_TIMES)){
				IToken opToken = token;
				IExpression rightExpr = expression(); 
				return factory.newArithmeticExpression(leftExpr, opToken, rightExpr);
			}
			else{
				return leftExpr;
			}
			// expression with paranthesis
		} else if (test(TC_IDENTIFIER)) {
			IVariable var = variable();
			if (test(TK_MINUS) || test(TK_PLUS) || test(TK_DIVIDE)
					|| test(TK_TIMES)) {
				IToken opToken = token;
				if (test(TC_IDENTIFIER)) {
					IVariable right = variable();
					return factory.newArithmeticExpression(var, opToken, right);
				} else if (test(TC_LITERAL_DECIMAL)){
					ILiteral rightExpr = decimalLiteral();
					return factory.newArithmeticExpression(var, opToken,
							rightExpr);
				}
				else {
					check(TC_LITERAL_INTEGER);
					ILiteral rightExpr = integerLiteral();
					return factory.newArithmeticExpression(var, opToken,
							rightExpr);
				}
			}
			else {
				return var;
			}
		} else if (test(TC_LITERAL_DECIMAL)) {
			// decimal
			ILiteral leftExpr = decimalLiteral();
			if (test(TK_MINUS) || test(TK_PLUS) || test(TK_DIVIDE)
					|| test(TK_TIMES)) {
				IToken opToken = token;
				if (test(TC_IDENTIFIER)) {

					IVariable right = variable();

					return factory.newArithmeticExpression(leftExpr, opToken,
							right);
				}

				else {

					ILiteral rightExpr = decimalLiteral();

					return factory.newArithmeticExpression(leftExpr, opToken,
							rightExpr);
				}
			} else {

				return factory.newDecimal(Double.valueOf(token.getText()));
			}
		} else if (test(TC_LITERAL_INTEGER)) {
			ILiteral leftExpr = integerLiteral();
			// integer
			if (test(TK_MINUS) || test(TK_PLUS) || test(TK_DIVIDE)
					|| test(TK_TIMES)) {
				IToken opToken = token;
				if (!test(TC_LITERAL_INTEGER)) {
					IVariable right = variable();
					return factory.newArithmeticExpression(leftExpr, opToken,
							right);
				}
				else {
					ILiteral rightExpr = integerLiteral();
					return factory.newArithmeticExpression(leftExpr, opToken,
							rightExpr);
				}
			}
			else {
				return leftExpr;
			}
		}

		return null;
	}


	private ILiteral integerLiteral() {
		// TODO Auto-generated method stub
		return factory.newInteger(Integer.valueOf(token.getText()));
	}

	private ILiteral decimalLiteral() {
		// TODO Auto-generated method stub
		return factory.newDecimal(Double.valueOf(token.getText()));

	}

	private IAssignment assignExpression() throws ParserException {

		check(TC_IDENTIFIER);
		final IVariable left = variable();
		check(TK_EQ);
		final IExpression right = expression();
		check(TK_SEMICOLON);
		return factory.newAssignment(left, right);
	}

	private IWhile whileExpression() throws ParserException {
		check(TK_LPARENS);
		IBooleanExpression boolexpr = booleanExpression();
		check(TK_RPARENS);
		ICompoundStatement compStmt = compoundStatement();
		return factory.newWhile(boolexpr, compStmt);

	}

	private ICompoundStatement compoundStatement() throws ParserException {

		List<ISimpleStatement> statements = new ArrayList<ISimpleStatement>();
		check(TK_LBRACE);
		while (true) {
			final ISimpleStatement simpleStatement = simpleStatement();
			statements.add(simpleStatement);
			if (test(TK_RBRACE))
				break;
		}

		return factory.newCompoundStatement(statements);

	}

	private ISimpleStatement simpleStatement() throws ParserException {
		if (test(TK_RETURN)) {
			IReturn ret = returnExpression();
			return ret;
		} else if (test(TK_IF)) {

			IIfThenElse ifThenelse = ifThenElseExpression();
			return ifThenelse;

		} else if (test(TK_WHILE)) {

			IWhile retWhile = whileExpression();
			return retWhile;
		} else {

			IAssignment assignment = assignExpression();
			return assignment;
		}

	}

	private IIfThenElse ifThenElseExpression() throws ParserException {

		check(TK_LPARENS);
		IBooleanExpression boolexpr = booleanExpression();
		check(TK_RPARENS);
		ICompoundStatement compStmt = compoundStatement();
		if (!test(TK_ELSE)) {
			return factory.newIfThen(boolexpr, compStmt);
		} else {
			ICompoundStatement elseCompStmt = compoundStatement();
			return factory.newIfThenElse(boolexpr, compStmt, elseCompStmt);
		}
	}

	// remaining code to be written by you

}
